package GUI;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.GeneralPath;
import java.lang.reflect.InvocationTargetException;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.statistics.HistogramDataset;
import org.jfree.data.xy.IntervalXYDataset;
import org.jvnet.substance.skin.SubstanceEmeraldDuskLookAndFeel;
import org.jvnet.substance.skin.SubstanceRavenLookAndFeel;
import org.jvnet.substance.skin.SubstanceTwilightLookAndFeel;

import Config.Configure;
import Statistics.IDGraphToJungGraph.MyEdge;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationModel;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.VisualizationServer.Paintable;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.SatelliteVisualizationViewer;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer;


/**
 * @author Xenophon
 * @time 2010-1-8 This is the GUI Render of the application which gets all data
 *       from DataFrontier in the GUI package.
 */
public class IDGraphGUI<V, E> extends JFrame {
	DataFrontier dataFrontier;
	static final String configure_file_path = "./configure";
	static final String instructions = "<html>"
			+ "<b><h2><center>Instructions for Mouse Listeners</center></h2></b>"
			+ "<p>There are two modes, Transforming and Picking."
			+ "<p>The modes are selected with a combo box."
			+

			"<p><p><b>Transforming Mode:</b>"
			+ "<ul>"
			+ "<li>Mouse1+drag pans the graph"
			+ "<li>Mouse1+Shift+drag rotates the graph"
			+ "<li>Mouse1+CTRL(or Command)+drag shears the graph"
			+ "</ul>"
			+

			"<b>Picking Mode:</b>"
			+ "<ul>"
			+ "<li>Mouse1 on a Vertex selects the vertex"
			+ "<li>Mouse1 elsewhere unselects all Vertices"
			+ "<li>Mouse1+Shift on a Vertex adds/removes Vertex selection"
			+ "<li>Mouse1+drag on a Vertex moves all selected Vertices"
			+ "<li>Mouse1+drag elsewhere selects Vertices in a region"
			+ "<li>Mouse1+Shift+drag adds selection of Vertices in a new region"
			+ "<li>Mouse1+CTRL on a Vertex selects the vertex and centers the display on it"
			+ "</ul>"
			+ "<b>Both Modes:</b>"
			+ "<ul>"
			+ "<li>Mousewheel scales with a crossover value of 1.0.<p>"
			+ "     - scales the graph layout when the combined scale is greater than 1<p>"
			+ "     - scales the graph view when the combined scale is less than 1";

	JDialog helpDialog;
	Paintable viewGrid;

	public IDGraphGUI(String title) {
		super(title);
		// Get data we need...
		Configure.SetConfigureFile(configure_file_path);
		try {
			Configure.GetConfigure();
		} catch (Exception e) {
			e.printStackTrace();
		}
		dataFrontier = new DataFrontier();
		Graph<String, MyEdge> graph = dataFrontier.GetJungFormatGraph();

		// Main Panel GUI
		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
		mainPanel.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
		add(mainPanel);
		
		//Some funny words here
		JLabel hint = new JLabel("I HATE BBS");
		hint.setBorder(BorderFactory.createEmptyBorder(0,25,0,0));
		mainPanel.add(hint);
		
		JSeparator sp = new JSeparator();
		sp.setForeground(Color.gray);
		mainPanel.add(sp);
		
		// Now Top Panel
		JPanel panel = new JPanel(new BorderLayout());
		Container rightPanel = new JPanel(new GridLayout(2, 1));

		// the preferred sizes for the two views
		Dimension preferredSize1 = new Dimension(800, 600);
		Dimension preferredSize2 = new Dimension(300, 300);

		// create one layout for the graph
		FRLayout<String, MyEdge> layout = new FRLayout<String, MyEdge>(graph);
		layout.setMaxIterations(500);

		// create one model that both views will share
		VisualizationModel<String, MyEdge> vm = new DefaultVisualizationModel<String, MyEdge>(
				layout, preferredSize1);

		// create 2 views that share the same model
		final VisualizationViewer<String, MyEdge> vv1 = new VisualizationViewer<String, MyEdge>(
				vm, preferredSize1);
		final SatelliteVisualizationViewer<String, MyEdge> vv2 = new SatelliteVisualizationViewer<String, MyEdge>(
				vv1, preferredSize2);
		
		
		vv1.setBackground(Color.black);
		vv1.getRenderContext().setEdgeDrawPaintTransformer(
				new PickableEdgePaintTransformer<MyEdge>(vv1
						.getPickedEdgeState(), Color.orange, Color.red));
		vv1.getRenderContext().setVertexFillPaintTransformer(
				new PickableVertexPaintTransformer<String>(vv1
						.getPickedVertexState(), Color.red, Color.yellow));
		vv2.getRenderContext().setEdgeDrawPaintTransformer(
				new PickableEdgePaintTransformer<MyEdge>(vv2
						.getPickedEdgeState(), Color.orange, Color.red));
		vv2.getRenderContext().setVertexFillPaintTransformer(
				new PickableVertexPaintTransformer<String>(vv2
						.getPickedVertexState(), Color.red, Color.yellow));
		vv1.getRenderer().setVertexRenderer(
				new GradientVertexRenderer<String, MyEdge>(Color.red,
						Color.white, true));
		vv1.getRenderContext()
				.setVertexLabelTransformer(new ToStringLabeller());
		vv1.getRenderer().getVertexLabelRenderer().setPosition(
				Renderer.VertexLabel.Position.SE);
				
		/*
		vv1.setBackground(Color.white);
        vv1.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<MyEdge>(vv1.getPickedEdgeState(), Color.black, Color.cyan));
        vv1.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer<String>(vv1.getPickedVertexState(), Color.red, Color.yellow));
        vv2.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<MyEdge>(vv2.getPickedEdgeState(), Color.black, Color.cyan));
        vv2.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer<String>(vv2.getPickedVertexState(), Color.red, Color.yellow));
        vv1.getRenderer().setVertexRenderer(new GradientVertexRenderer<String,MyEdge>(Color.red, Color.white, true));
        vv1.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        vv1.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
		*/
		
		ScalingControl vv2Scaler = new CrossoverScalingControl();
		vv2.scaleToLayout(vv2Scaler);

		viewGrid = new ViewGrid(vv2, vv1);

		// add default listener for ToolTips
		vv1.setVertexToolTipTransformer(new ToStringLabeller());
		vv2.setVertexToolTipTransformer(new ToStringLabeller());

		vv2.getRenderContext().setVertexLabelTransformer(
				vv1.getRenderContext().getVertexLabelTransformer());

		ToolTipManager.sharedInstance().setDismissDelay(10000);

		GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv1);
		panel.add(gzsp);
		rightPanel.add(new JPanel());
		rightPanel.add(vv2);
		panel.add(rightPanel, BorderLayout.EAST);
		panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		mainPanel.add(panel);

		helpDialog = new JDialog();
		helpDialog.getContentPane().add(new JLabel(instructions));

		// create a GraphMouse for the main view
		// 
		final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
		vv1.setGraphMouse(graphMouse);

		final ScalingControl scaler = new CrossoverScalingControl();

		JButton plus = new JButton("+");
		plus.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				scaler.scale(vv1, 1.1f, vv1.getCenter());
			}
		});
		JButton minus = new JButton("-");
		minus.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				scaler.scale(vv1, 1 / 1.1f, vv1.getCenter());
			}
		});

		JComboBox modeBox = graphMouse.getModeComboBox();
		modeBox.addItemListener(((DefaultModalGraphMouse) vv2.getGraphMouse())
				.getModeListener());

		JCheckBox gridBox = new JCheckBox("Show Grid");
		gridBox.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				showGrid(vv2, e.getStateChange() == ItemEvent.SELECTED);
			}
		});
		JButton help = new JButton("Help");
		help.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				helpDialog.pack();
				helpDialog.setVisible(true);
			}
		});

		JSeparator sp2 = new JSeparator();
		sp2.setForeground(Color.gray);
		mainPanel.add(sp2);
		
		// The helper bar
		JPanel controls = new JPanel();
		controls.add(plus);
		controls.add(minus);
		controls.add(modeBox);
		controls.add(gridBox);
		controls.add(help);
		mainPanel.add(controls);

		JSeparator sp3 = new JSeparator();
		sp3.setForeground(Color.gray);
		mainPanel.add(sp3);
		
		// Now we create id graph statistical chart
		JPanel chart = createChartContainer();
		chart.setPreferredSize(new Dimension(500, 270));
		chart.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		mainPanel.add(chart);

	}

	protected void showGrid(VisualizationViewer vv, boolean state) {
		if (state == true) {
			vv.addPreRenderPaintable(viewGrid);
		} else {
			vv.removePreRenderPaintable(viewGrid);
		}
		vv.repaint();
	}

	private IntervalXYDataset createDataset() {
		HistogramDataset localHistogramDataset = new HistogramDataset();
		double[] arrayOfDouble = new double[1000];
		Random localRandom = new Random(12345678L);
		for (int i = 0; i < 1000; ++i)
			arrayOfDouble[i] = (localRandom.nextGaussian() + 5.0D);
		localHistogramDataset.addSeries("Indegree", arrayOfDouble, 100, 2.0D, 8.0D);
		arrayOfDouble = new double[1000];
		for (int i = 0; i < 1000; ++i)
			arrayOfDouble[i] = (localRandom.nextGaussian() + 7.0D);
		localHistogramDataset.addSeries("Outdegree", arrayOfDouble, 100, 4.0D, 10.0D);
	
		return localHistogramDataset;
	}

	private JFreeChart createChart(IntervalXYDataset paramIntervalXYDataset) {
		JFreeChart localJFreeChart = ChartFactory.createHistogram(
				"Degree Distribution", null, null, paramIntervalXYDataset,
				PlotOrientation.VERTICAL, true, true, false);
		XYPlot localXYPlot = (XYPlot) localJFreeChart.getPlot();
		localXYPlot.setDomainPannable(true);
		localXYPlot.setRangePannable(true);
		localXYPlot.setForegroundAlpha(0.85F);
		NumberAxis localNumberAxis = (NumberAxis) localXYPlot.getRangeAxis();
		localNumberAxis.setStandardTickUnits(NumberAxis
				.createIntegerTickUnits());
		XYBarRenderer localXYBarRenderer = (XYBarRenderer) localXYPlot
				.getRenderer();
		localXYBarRenderer.setDrawBarOutline(false);
		localXYBarRenderer.setBarPainter(new StandardXYBarPainter());
		localXYBarRenderer.setShadowVisible(false);
		return localJFreeChart;
	}

	public JPanel createChartContainer() {
		JFreeChart localJFreeChart = createChart(createDataset());
		ChartPanel localChartPanel = new ChartPanel(localJFreeChart);
		localChartPanel.setMouseWheelEnabled(true);
		return localChartPanel;
	}

	/**
	 * draws a grid on the SatelliteViewer's lens
	 * 
	 * @author Tom Nelson
	 * 
	 */
	public static class ViewGrid implements Paintable {

		VisualizationViewer master;
		VisualizationViewer vv;

		public ViewGrid(VisualizationViewer vv, VisualizationViewer master) {
			this.vv = vv;
			this.master = master;
		}

		public void paint(Graphics g) {
			ShapeTransformer masterViewTransformer = master.getRenderContext()
					.getMultiLayerTransformer().getTransformer(Layer.VIEW);
			ShapeTransformer masterLayoutTransformer = master
					.getRenderContext().getMultiLayerTransformer()
					.getTransformer(Layer.LAYOUT);
			ShapeTransformer vvLayoutTransformer = vv.getRenderContext()
					.getMultiLayerTransformer().getTransformer(Layer.LAYOUT);

			Rectangle rect = master.getBounds();
			GeneralPath path = new GeneralPath();
			path.moveTo(rect.x, rect.y);
			path.lineTo(rect.width, rect.y);
			path.lineTo(rect.width, rect.height);
			path.lineTo(rect.x, rect.height);
			path.lineTo(rect.x, rect.y);

			for (int i = 0; i <= rect.width; i += rect.width / 10) {
				path.moveTo(rect.x + i, rect.y);
				path.lineTo(rect.x + i, rect.height);
			}
			for (int i = 0; i <= rect.height; i += rect.height / 10) {
				path.moveTo(rect.x, rect.y + i);
				path.lineTo(rect.width, rect.y + i);
			}
			Shape lens = path;
			lens = masterViewTransformer.inverseTransform(lens);
			lens = masterLayoutTransformer.inverseTransform(lens);
			lens = vvLayoutTransformer.transform(lens);
			Graphics2D g2d = (Graphics2D) g;
			Color old = g.getColor();
			g.setColor(Color.cyan);
			g2d.draw(lens);

			path = new GeneralPath();
			path.moveTo((float) rect.getMinX(), (float) rect.getCenterY());
			path.lineTo((float) rect.getMaxX(), (float) rect.getCenterY());
			path.moveTo((float) rect.getCenterX(), (float) rect.getMinY());
			path.lineTo((float) rect.getCenterX(), (float) rect.getMaxY());
			Shape crosshairShape = path;
			crosshairShape = masterViewTransformer
					.inverseTransform(crosshairShape);
			crosshairShape = masterLayoutTransformer
					.inverseTransform(crosshairShape);
			crosshairShape = vvLayoutTransformer.transform(crosshairShape);
			g.setColor(Color.black);
			g2d.setStroke(new BasicStroke(3));
			g2d.draw(crosshairShape);

			g.setColor(old);
		}

		public boolean useTransform() {
			return true;
		}
		
		public static void main(String[] args)
		{
			final IDGraphGUI myGUI = new IDGraphGUI("AmoAnalyzer");
			Runnable worker = new Runnable() {
				public void run() {
					try {						
						UIManager.setLookAndFeel(new SubstanceEmeraldDuskLookAndFeel());
						//UIManager.setLookAndFeel(new SubstanceTwilightLookAndFeel());
						JFrame.setDefaultLookAndFeelDecorated(true);
						JDialog.setDefaultLookAndFeelDecorated(true);
						myGUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
						myGUI.pack();
						myGUI.setVisible(true);
						myGUI.setExtendedState(JFrame.MAXIMIZED_BOTH);  
						//UIManager.setLookAndFeel(new SubstanceRavenLookAndFeel());
					} catch (UnsupportedLookAndFeelException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			};
			
			try {
				SwingUtilities.invokeAndWait(worker);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		
	}

}
